<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/css/options.css" /> 
    <script>
      var oFavicon = document.createElement("link");
          oParent = document.getElementsByTagName('head')[0];
      oFavicon.rel = "icon";
      oFavicon.href = chrome.extension.getURL("/png/icon16.png");
      oParent.appendChild(oFavicon);
      showModalDialog("javascript:setTimeout('window.close()', 10)");
    </script>
    <script type="text/javascript" src="/js/utils.js"></script>
    <script type="text/javascript" src="/js/extension_utils.js"></script>
    <script type="text/javascript" src="/js/extension_utils_requests.js"></script>
    <script type="text/javascript" src="/js/getEventKeyDescription.js"></script>
    <script>
      var giUIDCounter = 0;
      var goStatusElement = undefined;
      var gaValuesChangedHandlers = [];
      function initializeOptionsPage() {
        createOptionsPage();
        function optionsChanged() {
          for (var i = 0; i < gaValuesChangedHandlers.length; i++) {
            gaValuesChangedHandlers[i]();
          }
        }
        function showStatus(sMessage) {
          goStatusElement.innerText = sMessage;
        }
        
        function createErrorPage(sErrorMessage) {
          var oErrorMessageElement = document.createElement("span");
          oErrorMessageElement.innerText = sErrorMessage;
          document.body.appendChild(oErrorMessageElement);
          
        }
        function createOptionsPage() {
          var sIcon = null;
          if (goManifest.icons) {
            var iClosestTo96 = null;
            for (i in goManifest.icons) {
              if (iClosestTo96 == null || i < 96 ? i > iClosestTo96 : i < iClosestTo96) {
                iClosestTo96 = i;
                sIcon = goManifest.icons[iClosestTo96];
              }
            }
          }
          // Create a label for the main group:
          var oMainLabel = document.createElement("legend");
          oMainLabel.innerText = goManifest.name;
          var oMainTable = document.createElement('table');
          var oMainTableRow = document.createElement('tr');
          if (sIcon != null) {
            var oIconColumn = document.createElement('td');
            oIconColumn.className = 'icon';
            var oIconImg = document.createElement('img');
            oIconImg.src = chrome.extension.getURL(sIcon);
            oIconImg.className = 'icon';
            oIconColumn.appendChild(oIconImg);
            oMainTableRow.appendChild(oIconColumn);
          }
          var oDescriptionColumn = document.createElement('td');
          oDescriptionColumn.innerHTML = goManifest.description + '<br/>Version ' + goManifest.version;
          oMainTableRow.appendChild(oDescriptionColumn);
          oMainTable.appendChild(oMainTableRow);
          // Create a fieldset to store this group in.
          var oMainContainer = document.createElement("fieldset");
          oMainContainer.appendChild(oMainLabel);
          oMainContainer.appendChild(oMainTable);
            
          // Generate a table for input:
          var oInputTable = generateMenu(goOptionsStructure, goOptions);
          // create a table row and column to put the menu in:
          var oInputColumn = document.createElement("td");
          oInputColumn.colSpan = 4;
          oInputColumn.appendChild(oInputTable);
          var oInputRow = document.createElement("tr");
          oInputRow.appendChild(oInputColumn);
          // create a columns and buttons to save/load/etc...:
          var oLoadButton = document.createElement("button");
          var oOkButton = document.createElement("button");
          var oCancelButton = document.createElement("button");
          var oApplyButton = document.createElement("button");
          var oButtonsColumn = document.createElement("td");
          oButtonsColumn.colspan = 4;
          oButtonsColumn.className = "button-column";
          oButtonsColumn.appendChild(oLoadButton);
          oButtonsColumn.appendChild(oOkButton);
          oButtonsColumn.appendChild(oCancelButton);
          oButtonsColumn.appendChild(oApplyButton);
          // Create a row to store the buttons in:
          var oButtonsRow = document.createElement("tr");
          oButtonsRow.appendChild(oButtonsColumn);
          // Create a row to store the status in:
          var oStatusRow = document.createElement("tr");
          var oStatusColumn = document.createElement("td");
          oStatusColumn.colSpan = 4;
          goStatusElement = oStatusColumn;
          oStatusRow.appendChild(oStatusColumn);
          // Create a table to store the menu and buttons in:
          var oMenuTable = document.createElement("table");
          oMenuTable.cellPadding = 0;
          oMenuTable.cellSpacing = 0;
          oMenuTable.appendChild(oInputRow);
          oMenuTable.appendChild(oButtonsRow);
          oMenuTable.appendChild(oStatusRow);
          oMainContainer.appendChild(oMenuTable);
          document.body.appendChild(oMainContainer);
          // Add functionality to buttons:
          oLoadButton.innerText = "Undo";
          oLoadButton.disabled = true;
          oLoadButton.onclick = function () {
            showStatus("Reloading original option values...");
            loadOptions(
                function() {
                  var oNewInputTable = generateMenu(goOptionsStructure, goOptions);
                  oInputColumn.replaceChild(oNewInputTable, oInputTable);
                  oInputTable = oNewInputTable;
                  oLoadButton.disabled = true;
                  oApplyButton.disabled = true;
                  showStatus("Changes to option values undone.");
                }, function (sErrorMessage) {
                  showStatus("Reloading original option values failed: " + sErrorMessage);
                }
            );
          }
          oOkButton.innerText = "Ok";
          oOkButton.onclick = function () {
            showStatus("Saving option values...");
            saveOptions(function() {
              showStatus("Changes to option values saved.");
              window.close();
            }, function(sErrorMessage) {
              showStatus("Saving option values failed: " + sErrorMessage);
            });
          }
          oCancelButton.innerText = "Cancel";
          oCancelButton.onclick = function () {
            window.close();
          }
          oApplyButton.innerText = "Apply";
          oApplyButton.disabled = true;
          oApplyButton.onclick = function () {
            showStatus("Saving option values...");
            saveOptions(function() {
              showStatus("Changes to option values saved.");
              oLoadButton.disabled = true;
              oApplyButton.disabled = true;
            }, function(sErrorMessage) {
              showStatus("Saving option values failed: " + sErrorMessage);
            });
          }
          gaValuesChangedHandlers.push(function() {
            showStatus("Changes have not yet been saved!");
            oLoadButton.disabled = false;
            oApplyButton.disabled = false;
          });
        }
        function generateMenu(oMemberStructures, oValuesRoot) {
          var oMenuTable = document.createElement("table");
          oMenuTable.cellPadding = 0;
          oMenuTable.cellSpacing = 0;
          for (var sMemberName in oMemberStructures) {
            var oMenuRow = document.createElement("tr");
            var oMemberStructure = oMemberStructures[sMemberName];
            switch (oMemberStructure.type) {
              case "boolean":
                generateBooleanOptionMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "group":
                generateOptionsGroupMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "integer":
                generateIntegerOptionMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "key description":
                generateKeyDescriptionOptionMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "list":
                generateOptionsListMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "number":
                generateNumberOptionMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              case "text":
                generateTextOptionMenu(oMemberStructure, oValuesRoot, sMemberName, oMenuRow);
                break;
              default:
                assertionError("Unknown option type " + repr(oMemberStructure.type) + ".");
            }
            oMenuTable.appendChild(oMenuRow);
          }
          return oMenuTable;
        }
        function generateOptionsGroupMenu(oOptionsGroupInfo, oValuesRoot, sMemberName, oRow) {
          // Create a label for this group:
          var oGroupLabel = document.createElement("legend");
          oGroupLabel.innerText = oOptionsGroupInfo.label;
          // Create a tabel for this group with the menu:
          if (!oValuesRoot[sMemberName]) {
            oValuesRoot[sMemberName] = {};
            console.log("Creating menu for new options group " + repr(sMemberName) + ".");
          } else {
            console.log("Creating menu for existing options group " + repr(sMemberName) + " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          var oGroupTable = generateMenu(oOptionsGroupInfo.members, oValuesRoot[sMemberName]);
          // Create a fieldset to store this group in.
          var oGroupContainer = document.createElement("fieldset");
          oGroupContainer.appendChild(oGroupLabel);
          oGroupContainer.appendChild(oGroupTable);
          // Create a column to store this group in.
          var oColumn = document.createElement("td");
          oColumn.colSpan = 4;
          oColumn.appendChild(oGroupContainer);
          // Add the column to the row
          oRow.appendChild(oColumn);
        }
        function generateTextOptionMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create the columns for this entry:
          var oHeaderColumn = document.createElement("td");
          var oLabelColumn = document.createElement("td");
          var oInputColumn = document.createElement("td");
          var oRemarksColumn = document.createElement("td");
          oHeaderColumn.className = "header";
          oLabelColumn.className = "label";
          oInputColumn.className = "input";
          oRemarksColumn.className = "remarks";
          oRow.appendChild(oHeaderColumn);
          oRow.appendChild(oLabelColumn);
          oRow.appendChild(oInputColumn);
          oRow.appendChild(oRemarksColumn);
          // Create a the label for this option:
          oLabelColumn.innerText = oStructure.label;
          // Create the input element  for this option:
          var oInputElement = document.createElement("input");
          oInputColumn.appendChild(oInputElement);
          oInputElement.type = "text";
          if (!oValuesRoot[sMemberName]) {
            oValuesRoot[sMemberName] = "";
            console.log("Creating menu for new text option " + repr(sMemberName) + ".");
          } else {
            oInputElement.value = oValuesRoot[sMemberName];
            console.log("Creating menu for existing text option " + repr(sMemberName) + 
                " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          // I wish there was an event that signals the value was changed. As there is none, we"ll check periodically for changes:
          setInterval(function () {
            if (oValuesRoot[sMemberName] != oInputElement.value) {
              oValuesRoot[sMemberName] = oInputElement.value;
              console.log("Changed text option " + repr(sMemberName) + " value to " + repr(oValuesRoot[sMemberName]) + ".");
              optionsChanged();
            }
          }, 100);
        }
        function generateKeyDescriptionOptionMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create the columns for this entry:
          var oHeaderColumn = document.createElement("td");
          var oLabelColumn = document.createElement("td");
          var oInputColumn = document.createElement("td");
          var oRemarksColumn = document.createElement("td");
          oHeaderColumn.className = "header";
          oLabelColumn.className = "label";
          oInputColumn.className = "input";
          oRemarksColumn.className = "remarks";
          oRow.appendChild(oHeaderColumn);
          oRow.appendChild(oLabelColumn);
          oRow.appendChild(oInputColumn);
          oRow.appendChild(oRemarksColumn);
          // Create a the label for this option:
          oLabelColumn.innerText = oStructure.label;
          // Create the input element  for this option:
          var oInputElement = document.createElement("input");
          oInputColumn.appendChild(oInputElement);
          oInputElement.type = "text";
          function keyEvent(event) {
            if (!event.repeat) {
              var sNewKeyDescription = getEventKeyDescription(event);
              if (sNewKeyDescription != null) {
                oInputElement.value = sNewKeyDescription;
                oValuesRoot[sMemberName] = sNewKeyDescription;
                optionsChanged();
                event.preventDefault();
                event.stopPropagation();
              } else {
//                alert('Sorry, that key or key combination is not supported :(');
              }
            }
          }
          // We need to register both keydown and keypress because we can only
          // a limited number of keys in each, but most keys by combining them.
          oInputElement.addEventListener("keydown", keyEvent, true);
          oInputElement.addEventListener("keypress", keyEvent, true);
          if (!oValuesRoot[sMemberName]) {
            oValuesRoot[sMemberName] = "";
            console.log("Creating menu for new key description option " + repr(sMemberName) + ".");
          } else {
            oInputElement.value = oValuesRoot[sMemberName];
            console.log("Creating menu for existing key description option " + repr(sMemberName) + 
                " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          // I wish there was an event that signals the value was changed. As there is none, we"ll check periodically for changes:
          // (The user could use the mouse to change the contents, which we"ll want to undo for esthetics.
          setInterval(function () {
            if (oInputElement.value != oValuesRoot[sMemberName]) {
              oInputElement.value = oValuesRoot[sMemberName];
              console.log("Reverted changed key description option " + repr(sMemberName) + " value to " + repr(oValuesRoot[sMemberName]) + ".");
            }
          }, 100);
        }
        function generateNumberOptionMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create the columns for this entry:
          var oHeaderColumn = document.createElement("td");
          var oLabelColumn = document.createElement("td");
          var oInputColumn = document.createElement("td");
          var oRemarksColumn = document.createElement("td");
          oHeaderColumn.className = "header";
          oLabelColumn.className = "label";
          oInputColumn.className = "input";
          oRemarksColumn.className = "remarks";
          oRow.appendChild(oHeaderColumn);
          oRow.appendChild(oLabelColumn);
          oRow.appendChild(oInputColumn);
          oRow.appendChild(oRemarksColumn);
          // Create a the label for this option:
          oLabelColumn.innerText = oStructure.label;
          // Create the input element  for this option:
          var oInputElement = document.createElement("input");
          oInputColumn.appendChild(oInputElement);
          oInputElement.type = "text";
          if (!oValuesRoot[sMemberName]) {
            oValuesRoot[sMemberName] = "";
            console.log("Creating menu for new number option " + repr(sMemberName) + ".");
          } else {
            oInputElement.value = oValuesRoot[sMemberName];
            console.log("Creating menu for existing number option " + repr(sMemberName) + 
                " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          // I wish there was an event that signals the value was changed. As there is none, we"ll check periodically for changes:
          setInterval(function () {
            if (oValuesRoot[sMemberName] != oInputElement.value) {
              oValuesRoot[sMemberName] = oInputElement.value;
              console.log("Changed number option " + repr(sMemberName) + " value to " + repr(oValuesRoot[sMemberName]) + ".");
              optionsChanged();
            }
          }, 100);
        }
        function generateIntegerOptionMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create the columns for this entry:
          var oHeaderColumn = document.createElement("td");
          var oLabelColumn = document.createElement("td");
          var oInputColumn = document.createElement("td");
          var oRemarksColumn = document.createElement("td");
          oHeaderColumn.className = "header";
          oLabelColumn.className = "label";
          oInputColumn.className = "input";
          oRemarksColumn.className = "remarks";
          oRow.appendChild(oHeaderColumn);
          oRow.appendChild(oLabelColumn);
          oRow.appendChild(oInputColumn);
          oRow.appendChild(oRemarksColumn);
          // Create a the label for this option:
          oLabelColumn.innerText = oStructure.label;
          // Create the input element  for this option:
          var oInputElement = document.createElement("input");
          oInputColumn.appendChild(oInputElement);
          oInputElement.type = "text";
          if (!oValuesRoot[sMemberName]) {
            oValuesRoot[sMemberName] = "";
            console.log("Creating menu for new integer option " + repr(sMemberName) + ".");
          } else {
            oInputElement.value = oValuesRoot[sMemberName];
            console.log("Creating menu for existing integer option " + repr(sMemberName) + 
                " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          // I wish there was an event that signals the value was changed. As there is none, we"ll check periodically for changes:
          setInterval(function () {
            if (oValuesRoot[sMemberName] != oInputElement.value) {
              oValuesRoot[sMemberName] = oInputElement.value;
              console.log("Changed integer option " + repr(sMemberName) + " value to " + repr(oValuesRoot[sMemberName]) + ".");
              optionsChanged();
            }
          }, 100);
        }
        function generateBooleanOptionMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create the columns for this entry:
          var oHeaderColumn = document.createElement("td");
          var oLabelColumn = document.createElement("td");
          var oInputColumn = document.createElement("td");
          var oRemarksColumn = document.createElement("td");
          oHeaderColumn.className = "header";
          oLabelColumn.className = "label";
          oInputColumn.className = "input";
          oRemarksColumn.className = "remarks";
          oRow.appendChild(oHeaderColumn);
          oRow.appendChild(oLabelColumn);
          oRow.appendChild(oInputColumn);
          oRow.appendChild(oRemarksColumn);
          // Create a the label for this option:
          var oLabel = document.createElement("label");
          oLabel.htmlFor = "checkbox" + (++giUIDCounter);
          oLabel.innerText = oStructure.label;
          oLabelColumn.appendChild(oLabel);
          // Create a the input element for this option:
          var oInputElement = document.createElement("input");
          oInputColumn.appendChild(oInputElement);
          oInputElement.type = "checkbox";
          oInputElement.id = "checkbox" + giUIDCounter;
          if (typeof(oValuesRoot[sMemberName]) == "undefined") {
            console.log("Creating menu for new boolean option " + repr(sMemberName) + ".");
          } else {
            oInputElement.checked = oValuesRoot[sMemberName];
            console.log("Creating menu for existing boolean option " + repr(sMemberName) + 
                " (value=" + repr(oValuesRoot[sMemberName]) + ").");
          }
          // I wish there was an event that signals the value was changed. As there is none, we"ll check periodically for changes:
          oInputElement.onchange = function () {
            if (oValuesRoot[sMemberName] != oInputElement.checked) {
              oValuesRoot[sMemberName] = oInputElement.checked;
              console.log("Changed boolean option " + repr(sMemberName) + " value to " + repr(oValuesRoot[sMemberName]) + ".");
              optionsChanged();
            }
          };
        }
  
        function generateOptionsListEntryMenu(oEntryStructures, oValuesRoot, iMemberNumber, oTable) {
          // Create a row to contain the entry:
          var oRow = document.createElement("tr");
          oTable.appendChild(oRow);
          // Create a table to contain the entries values:
          if (!oValuesRoot[iMemberNumber]) {
            oValuesRoot[iMemberNumber] = {};
            console.log("Creating menu for new options list entry #" + iMemberNumber + "/" + oValuesRoot.length + ".");
          } else {
            console.log("Creating menu for existing options list entry #" + iMemberNumber + "/" + oValuesRoot.length + ".");
          }
          var oValue = oValuesRoot[iMemberNumber];
          var oMenuTable = generateMenu(oEntryStructures, oValue);
          // Create a button to remove the entry:
          var oRemoveButton = document.createElement("button");
          oRemoveButton.innerText = "-";
          oRemoveButton.onclick = function() {
            for (var i = 0; i < oValuesRoot.length; i++) {
              if (oValue == oValuesRoot[i]) {
                oValuesRoot.splice(i, 1);
                break;
              }
            }
            assert(i <= oValuesRoot.length, "Could not find ListEntry among all " + oValuesRoot.length + " items!");
            oTable.removeChild(oRow);
            console.log("remove ListEntry #" + i + ", " + oValuesRoot.length + " items remaining.");
            optionsChanged();
          }
          // Create columns for the remove button and the menu table:
          var oRemoveButtonColumn = document.createElement("td");
          oRemoveButtonColumn.appendChild(oRemoveButton);
          var oMenuColumn = document.createElement("td");
          oMenuColumn.appendChild(oMenuTable);
          // Add the columns to the row we created earlier:
          oRow.appendChild(oRemoveButtonColumn);
          oRow.appendChild(oMenuColumn);
          console.log("created ListEntry #" + iMemberNumber + "/" + oValuesRoot.length + ".");
        }
        function generateOptionsListMenu(oStructure, oValuesRoot, sMemberName, oRow) {
          // Create a label for this list:
          var oListLabel = document.createElement("legend");
          oListLabel.innerText = oStructure.label;
          // Create a tabel and store the list entries:
          var oListTable = document.createElement("table");
          oListTable.cellPadding = 0;
          oListTable.cellSpacing = 0;
          if (!oValuesRoot[sMemberName]) oValuesRoot[sMemberName] = [];
          for (var i = 0; i < oValuesRoot[sMemberName].length; i++) {
            generateOptionsListEntryMenu(oStructure.items, oValuesRoot[sMemberName], i, oListTable);
          }
          // Create an add button:
          var oListAddButton = document.createElement("button");
          oListAddButton.innerText = "+";
          oListAddButton.onclick = function() {
            var i = oValuesRoot[sMemberName].length;
            generateOptionsListEntryMenu(oStructure.items, oValuesRoot[sMemberName], i, oListTable);
            optionsChanged();
          }
          // Combine label, table of entries and add button into one:
          var oListContainer = document.createElement("fieldset");
          oListContainer.appendChild(oListLabel);
          oListContainer.appendChild(oListTable);
          oListContainer.appendChild(oListAddButton);
          // Add result to Row:
          var oColumn = document.createElement("td");
          oColumn.colSpan = 4;
          oColumn.appendChild(oListContainer);
          oRow.appendChild(oColumn);
        }
      }
    </script>
  </head>
  <body onload="connectToBackground(initializeOptionsPage)">
  </body>
</html>
